home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
txl
/
c!!20.lha
/
C++2.0
/
groff.C++
next >
Wrap
C/C++ Source or Header
|
1992-02-26
|
19KB
|
801 lines
// -*- C++ -*-
/* Copyright (C) 1990, 1991 Free Software Foundation, Inc.
Written by James Clark (jjc@jclark.uucp)
This file is part of groff.
groff is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 1, or (at your option) any later
version.
groff is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with groff; see the file LICENSE. If not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
// A C++ implementation of groff.sh.
#include <stdio.h>
#include <string.h>
#include <osfcn.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <sys/types.h>
#ifndef NO_SYS_WAIT_H
#include <sys/wait.h>
#endif /* not NO_SYS_WAIT_H */
#include "lib.h"
#include "assert.h"
#include "errarg.h"
#include "error.h"
#include "stringclass.h"
#include "font.h"
#include "device.h"
#ifdef HAVE_PID_T
typedef pid_t PID_T;
#else
typedef int PID_T;
#endif
#ifdef HAVE_UNION_WAIT
typedef union wait WAIT_STATUS_T;
#else
typedef int WAIT_STATUS_T;
#endif
#ifdef NO_SYS_WAIT_H
extern "C" {
PID_T wait(WAIT_STATUS_T *);
}
#endif /* NO_SYS_WAIT_H */
#ifdef HAVE_UNION_WAIT
/* Convert the argument which must be an lvalue of type union wait *
to an int. */
#define WTOI(s) (*(int *)&(s))
#else
#define WTOI(s) (s)
#endif
#ifndef WIFEXITED
#define WIFEXITED(s) ((WTOI(s) & 0377) == 0)
#define WIFSTOPPED(s) ((WTOI(s) & 0377) == 0177)
#define WIFSIGNALED(s) ((WTOI(s) & 0377) != 0 && ((WTOI(s) & 0377) != 0177))
#endif /* WIFEXITED */
#ifndef WEXITSTATUS
#define WEXITSTATUS(s) ((WTOI(s) >> 8) & 0377)
#define WTERMSIG(s) (WTOI(s) & 0177)
#define WSTOPSIG(s) ((WTOI(s) >> 8) & 0377)
#endif /* not WEXITSTATUS */
#ifdef WAIT_COREDUMP_0200
#define WCOREDUMPED(s) (WTOI(s) & 0200)
#else
#define WCOREDUMPED(s) 0
#endif
const char *strsignal(int);
const char *ps_print[] = { (PSPRINT) 0 };
const char *dvi_print[] = { (DVIPRINT) 0 };
const char *grops[] = { "grops", 0 };
const char *grotty[] = { "grotty", 0 };
const char *grodvi[] = { "grodvi", 0 };
const char *gxditview[] = { "gxditview", "-", 0 };
struct {
const char *pseudo_name;
const char *real_name;
const char *description; // Description of device (used in help message).
const char **driver; // Driver argument vector.
unsigned flags; // Bitwise OR of
#define EQN_D_OPTION 01 /* Add -D option to eqn. */
#define PIC_X_OPTION 02 /* Add -x option to pic. */
#define PIC_P_OPTION 04 /* Add -p option to pic. */
#define GROFF_OPTIONS 010 /* Driver understands -F and -v options. */
#define XT_OPTION 020 /* Driver understands -title and -xrm options. */
#define PRINT_OPTION 040 /* Understands -printCommand. */
const char *macro_file; // -m option to pass to troff
const char **spooler; // Spooler argument vector.
} device_table[] = {
{
"ps",
"ps",
"PostScript",
grops,
EQN_D_OPTION|PIC_X_OPTION|PIC_P_OPTION|GROFF_OPTIONS,
"ps",
ps_print,
},
{
"ascii",
"ascii",
"ASCII",
grotty,
GROFF_OPTIONS,
"tty",
0
},
{
"latin1",
"latin1",
"ISO Latin-1",
grotty,
GROFF_OPTIONS,
"tty",
0
},
{
"dvi",
"dvi",
"TeX dvi format",
grodvi,
GROFF_OPTIONS|PIC_X_OPTION,
"dvi",
dvi_print,
},
{
"X100",
"X100",
"X11 previewer at 100dpi",
gxditview,
EQN_D_OPTION|PIC_X_OPTION|XT_OPTION,
"X",
0
},
{
"X75",
"X75",
"X11 previewer at 75dpi",
gxditview,
EQN_D_OPTION|PIC_X_OPTION|XT_OPTION,
"X",
0
},
{
"X100-12",
"X100-12",
"X11 previewer at 100dpi (optimized for 12 point text)",
gxditview,
EQN_D_OPTION|PIC_X_OPTION|XT_OPTION,
"X",
0
},
{
"X75-12",
"X75-12",
"X11 previewer at 75dpi (optimized for 12 point text)",
gxditview,
EQN_D_OPTION|PIC_X_OPTION|XT_OPTION,
"X",
0
},
{
"Xps",
"ps",
"X11 previewer with PostScript metrics",
gxditview,
EQN_D_OPTION|PIC_X_OPTION|PIC_P_OPTION|XT_OPTION|PRINT_OPTION,
"Xps",
0
},
};
const int SOELIM_INDEX = 0;
const int REFER_INDEX = SOELIM_INDEX + 1;
const int PIC_INDEX = REFER_INDEX + 1;
const int TBL_INDEX = PIC_INDEX + 1;
const int EQN_INDEX = TBL_INDEX + 1;
const int TROFF_INDEX = EQN_INDEX + 1;
const int POST_INDEX = TROFF_INDEX + 1;
const int SPOOL_INDEX = POST_INDEX + 1;
const int NCOMMANDS = SPOOL_INDEX + 1;
// Children exit with this status if the execvp failed.
const int EXEC_FAILED_EXIT_STATUS = 0xff;
class possible_command {
char *name;
string args;
char **argv;
public:
PID_T pid;
possible_command();
~possible_command();
void set_name(const char *);
const char *get_name();
void append_arg(const char *, const char * = 0);
void prepend_arg(const char *, const char * = 0);
void execp();
void clear_args();
void build_argv();
void print(int is_last, FILE *fp);
};
int lflag = 0;
possible_command commands[NCOMMANDS];
int run_commands();
void print_commands();
void append_arg_to_string(const char *arg, string &str);
void usage();
void help();
void devices();
void sys_fatal(const char *);
int main(int argc, char **argv)
{
program_name = argv[0];
static char stderr_buf[BUFSIZ];
setbuf(stderr, stderr_buf);
string Pargs, Largs;
int Vflag = 0;
int zflag = 0;
int iflag = 0;
int opt;
const char *device = getenv("GROFF_TYPESETTER");
if (!device)
device = DEVICE;
commands[TROFF_INDEX].set_name("gtroff");
while ((opt = getopt(argc, argv,
"itpeRszavVhblCENZH:F:m:T:f:w:W:M:d:r:n:o:P:L:"))
!= EOF) {
char buf[3];
buf[0] = '-';
buf[1] = opt;
buf[2] = '\0';
switch (opt) {
case 'i':
iflag = 1;
break;
case 't':
commands[TBL_INDEX].set_name("gtbl");
break;
case 'p':
commands[PIC_INDEX].set_name("gpic");
break;
case 'e':
commands[EQN_INDEX].set_name("geqn");
break;
case 's':
commands[SOELIM_INDEX].set_name("gsoelim");
break;
case 'R':
commands[REFER_INDEX].set_name("grefer");
break;
case 'z':
case 'a':
commands[TROFF_INDEX].append_arg(buf);
// fall through
case 'Z':
zflag++;
break;
case 'l':
lflag++;
break;
case 'V':
Vflag++;
break;
case 'v':
commands[POST_INDEX].append_arg(buf);
// fall through
case 'C':
commands[SOELIM_INDEX].append_arg(buf);
commands[PIC_INDEX].append_arg(buf);
commands[TBL_INDEX].append_arg(buf);
commands[EQN_INDEX].append_arg(buf);
commands[TROFF_INDEX].append_arg(buf);
break;
case 'N':
commands[EQN_INDEX].append_arg(buf);
break;
case 'h':
help();
break;
case 'E':
case 'b':
commands[TROFF_INDEX].append_arg(buf);
break;
case 'T':
device = optarg;
break;
case 'F':
font::command_line_font_dir(optarg);
commands[POST_INDEX].append_arg(buf, optarg);
// fall through
case 'f':
case 'o':
case 'm':
case 'r':
case 'M':
case 'H':
case 'd':
case 'n':
case 'w':
case 'W':
commands[TROFF_INDEX].append_arg(buf, optarg);
break;
case 'P':
Pargs += optarg;
Pargs += '\0';
break;
case 'L':
Largs += optarg;
Largs += '\0';
break;
case '?':
usage();
break;
default:
assert(0);
break;
}
}
int found = 0;
for (int device_index = 0;
device_index < sizeof(device_table)/sizeof(device_table[0]);
device_index++)
if (strcmp(device, device_table[device_index].pseudo_name) == 0) {
found = 1;
break;
}
if (!found) {
error("unknown device `%1'", device);
devices();
exit(1);
}
device = device_table[device_index].real_name;
if (device_table[device_index].macro_file != 0)
commands[TROFF_INDEX].prepend_arg("-m",
device_table[device_index].macro_file);
commands[POST_INDEX].set_name(device_table[device_index].driver[0]);
if (!(device_table[device_index].flags & GROFF_OPTIONS))
commands[POST_INDEX].clear_args();
if ((device_table[device_index].flags & XT_OPTION)
&& argc - optind == 1) {
commands[POST_INDEX].append_arg("-title");
commands[POST_INDEX].append_arg(argv[optind]);
commands[POST_INDEX].append_arg("-xrm");
commands[POST_INDEX].append_arg("*iconName:", argv[optind]);
}
if (device_table[device_index].flags & PRINT_OPTION) {
found = 0;
for (int di = 0; di < sizeof(device_table)/sizeof(device_table[0]); di++) {
if (strcmp(device, device_table[di].pseudo_name) == 0) {
found = 1;
break;
}
}
assert(found);
string print_string(device_table[di].driver[0]);
int i;
for (i = 1; device_table[di].driver[i]; i++)
append_arg_to_string(device_table[di].driver[i], print_string);
if (device_table[di].spooler) {
print_string += " |";
for (i = 0; device_table[di].spooler[i]; i++)
append_arg_to_string(device_table[di].spooler[i], print_string);
}
print_string += '\0';
commands[POST_INDEX].append_arg("-printCommand");
commands[POST_INDEX].append_arg(print_string.contents());
}
const char *p = Pargs.contents();
const char *end = p + Pargs.length();
while (p < end) {
commands[POST_INDEX].append_arg(p);
p = strchr(p, '\0') + 1;
}
int i;
for (i = 1; device_table[device_index].driver[i]; i++)
commands[POST_INDEX].append_arg(device_table[device_index].driver[i]);
if (device_table[device_index].flags & PIC_X_OPTION)
commands[PIC_INDEX].append_arg("-x");
if (device_table[device_index].flags & PIC_P_OPTION)
commands[PIC_INDEX].append_arg("-p");
if (device_table[device_index].flags & EQN_D_OPTION)
commands[EQN_INDEX].append_arg("-D");
if (lflag && device_table[device_index].spooler) {
commands[SPOOL_INDEX].set_name(device_table[device_index].spooler[0]);
p = Largs.contents();
end = p + Largs.length();
while (p < end) {
commands[SPOOL_INDEX].append_arg(p);
p = strchr(p, '\0') + 1;
}
for (i = 1; device_table[device_index].spooler[i]; i++)
commands[SPOOL_INDEX].append_arg(device_table[device_index].spooler[i]);
}
if (zflag) {
commands[POST_INDEX].set_name(0);
commands[SPOOL_INDEX].set_name(0);
}
commands[TROFF_INDEX].append_arg("-T", device);
int have_eqnchar = 0;
if (commands[EQN_INDEX].get_name() != 0) {
commands[EQN_INDEX].append_arg("-T", device);
font::set_device_name(device);
char *path = 0;
FILE *fp = font::open_file("eqnchar", &path);
if (fp) {
fclose(fp);
if (path[0] == '-' && path[1] != '\0')
commands[EQN_INDEX].append_arg("--");
commands[EQN_INDEX].append_arg(path);
delete path;
have_eqnchar = 1;
}
}
for (int first_index = 0; first_index < TROFF_INDEX; first_index++)
if (commands[first_index].get_name() != 0)
break;
if (optind < argc) {
if (argv[optind][0] == '-' && argv[optind][1] != '\0'
&& (!have_eqnchar || first_index != EQN_INDEX))
commands[first_index].append_arg("--");
for (i = optind; i < argc; i++)
commands[first_index].append_arg(argv[i]);
if (iflag)
commands[first_index].append_arg("-");
}
if (have_eqnchar && (first_index != EQN_INDEX || optind >= argc))
commands[EQN_INDEX].append_arg("-");
if (Vflag) {
print_commands();
exit(0);
}
exit(run_commands());
}
void print_commands()
{
for (int last = SPOOL_INDEX; last >= 0; last--)
if (commands[last].get_name() != 0)
break;
for (int i = 0; i <= last; i++)
if (commands[i].get_name() != 0)
commands[i].print(i == last, stdout);
}
// Run the commands. Return the code with which to exit.
int run_commands()
{
for (int last = SPOOL_INDEX; last >= 0; last--)
if (commands[last].get_name() != 0)
break;
int last_input = 0;
int proc_count = 0;
for (int i = 0; i <= last; i++)
if (commands[i].get_name() != 0) {
proc_count++;
int pdes[2];
if (i != last) {
if (pipe(pdes) < 0)
sys_fatal("pipe");
}
#ifdef HAVE_VFORK
PID_T pid = vfork();
if (pid < 0)
sys_fatal("vfork");
#else /* !HAVE_VFORK */
PID_T pid = fork();
if (pid < 0)
sys_fatal("fork");
#endif /* !HAVE_VFORK */
if (pid == 0) {
// child
if (last_input != 0) {
if (close(0) < 0)
sys_fatal("close");
if (dup(last_input) < 0)
sys_fatal("dup");
if (close(last_input) < 0)
sys_fatal("close");
}
if (i != last) {
if (close(1) < 0)
sys_fatal("close");
if (dup(pdes[1]) < 0)
sys_fatal("dup");
if (close(pdes[1]) < 0)
sys_fatal("close");
if (close(pdes[0]))
sys_fatal("close");
}
commands[i].execp();
}
// in the parent
if (last_input != 0) {
if (close(last_input) < 0)
sys_fatal("close");
}
if (i != last) {
if (close(pdes[1]) < 0)
sys_fatal("close");
last_input = pdes[0];
}
commands[i].pid = pid;
}
int ret = 0;
while (proc_count > 0) {
WAIT_STATUS_T status;
PID_T pid = wait(&status);
if (pid < 0)
sys_fatal("wait");
for (i = 0; i < NCOMMANDS; i++)
if (commands[i].pid == pid) {
--proc_count;
if (WIFSIGNALED(status)) {
int sig = WTERMSIG(status);
if (sig != SIGPIPE) {
error("%1: %2%3",
commands[i].get_name(),
strsignal(sig),
WCOREDUMPED(status) ? " (core dumped)" : "");
ret |= 2;
}
}
else if (WIFEXITED(status)) {
int exit_status = WEXITSTATUS(status);
if (exit_status == EXEC_FAILED_EXIT_STATUS)
ret |= 4;
else if (exit_status != 0)
ret |= 1;
}
else
error("unexpected status %1", WTOI(status));
break;
}
}
return ret;
}
possible_command::possible_command()
: pid(-1), name(0), argv(0)
{
}
possible_command::~possible_command()
{
delete name;
delete argv;
}
void possible_command::set_name(const char *s)
{
name = strsave(s);
}
const char *possible_command::get_name()
{
return name;
}
void possible_command::clear_args()
{
args.clear();
}
void possible_command::append_arg(const char *s, const char *t)
{
args += s;
if (t)
args += t;
args += '\0';
}
void possible_command::prepend_arg(const char *s, const char *t)
{
string old(args);
args = s;
if (t)
args += t;
args += '\0';
args += old;
}
void possible_command::build_argv()
{
// Count the number of arguments.
int len = args.length();
int argc = 1;
char *p = 0;
if (len > 0) {
p = &args[0];
for (int i = 0; i < len; i++)
if (p[i] == '\0')
argc++;
}
// Build an argument vector.
argv = new char *[argc + 1];
argv[0] = name;
for (int i = 1; i < argc; i++) {
argv[i] = p;
p = strchr(p, '\0') + 1;
}
argv[argc] = 0;
}
void possible_command::print(int is_last, FILE *fp)
{
build_argv();
fputs(argv[0], fp);
string str;
for (int i = 1; argv[i] != 0; i++) {
str.clear();
append_arg_to_string(argv[i], str);
put_string(str, fp);
}
if (is_last)
putc('\n', fp);
else
fputs(" | ", fp);
}
void append_arg_to_string(const char *arg, string &str)
{
str += ' ';
int needs_quoting = 0;
int contains_single_quote = 0;
for (const char *p = arg; *p != '\0'; p++)
switch (*p) {
case ';':
case '&':
case '(':
case ')':
case '|':
case '^':
case '<':
case '>':
case '\n':
case ' ':
case '\t':
case '\\':
case '"':
case '$':
needs_quoting = 1;
break;
case '\'':
contains_single_quote = 1;
break;
}
if (contains_single_quote || arg[0] == '\0') {
str += '"';
for (p = arg; *p != '\0'; p++)
switch (*p) {
case '"':
case '\\':
case '$':
str += '\\';
// fall through
default:
str += *p;
break;
}
str += '"';
}
else if (needs_quoting) {
str += '\'';
str += arg;
str += '\'';
}
else
str += arg;
}
void possible_command::execp()
{
build_argv();
execvp(name, argv);
error("couldn't exec %1: %2", name, strerror(errno));
fflush(stderr); // just in case
_exit(EXEC_FAILED_EXIT_STATUS);
}
void synopsis()
{
fprintf(stderr,
"usage: %s [-abehilpstvzCENRVZ] [-Hfile] [-Fdir] [-mname] [-Tdev] [-ffam]\n"
" [-wname] [-Wname] [ -Mdir] [-dcs] [-rcn] [-nnum] [-olist] [-Parg]\n"
" [-Larg] [files...]\n",
program_name);
}
void devices()
{
fputs("Available devices are:\n", stderr);
for (int i = 0; i < sizeof(device_table)/sizeof(device_table[0]); i++)
fprintf(stderr, "%s\t%s\n",
device_table[i].pseudo_name,
device_table[i].description);
}
void help()
{
synopsis();
fputs("\n"
"-h\tprint this message\n"
"-t\tpreprocess with tbl\n"
"-p\tpreprocess with pic\n"
"-e\tpreprocess with eqn\n"
"-s\tpreprocess with soelim\n"
"-R\tpreprocess with refer\n"
"-Tdev\tuse device dev\n"
"-mname\tread macros tmac.name\n"
"-dcs\tdefine a string c as s\n"
"-rcn\tdefine a number register c as n\n"
"-nnum\tnumber first page n\n"
"-olist\toutput only pages in list\n"
"-ffam\tuse fam as the default font family\n"
"-Fdir\tsearch directory dir for device directories\n"
"-Mdir\tsearch dir for macro files\n"
"-Hfile\tread hyphenation patterns from file\n"
"-v\tprint version number\n"
"-z\tsuppress formatted output\n"
"-Z\tdon't postprocess\n"
"-a\tproduce ASCII description of output\n"
"-i\tread standard input after named input files\n"
"-wname\tenable warning name\n"
"-Wname\tinhibit warning name\n"
"-E\tinhibit all errors\n"
"-b\tprint backtraces with errors or warnings\n"
"-l\tspool the output\n"
"-C\tenable compatibility mode\n"
"-V\tprint commands on stdout instead of running them\n"
"-Parg\tpass arg to the postprocessor\n"
"-Larg\tpass arg to the spooler\n"
"-N\tdon't allow newlines within eqn delimiters\n"
"\n",
stderr);
devices();
exit(0);
}
void usage()
{
synopsis();
fprintf(stderr, "%s -h gives more help\n", program_name);
exit(1);
}
void sys_fatal(const char *s)
{
fatal("%1: %2", s, strerror(errno));
}
#ifdef HAVE_SYS_SIGLIST
extern "C" {
extern char *sys_siglist[];
}
#endif /* HAVE_SYS_SIGLIST */
const char *strsignal(int n)
{
static char buf[sizeof("Signal ") + 1 + INT_DIGITS];
#ifdef HAVE_SYS_SIGLIST
if (n >= 0 && n < NSIG && sys_siglist[n] != 0)
return sys_siglist[n];
#endif /* HAVE_SYS_SIGLIST */
sprintf(buf, "Signal %d", n);
return buf;
}